from flask import Flask, request, jsonify, render_template, send_from_directory, Response
from werkzeug.utils import safe_join
from flask_socketio import SocketIO, emit, join_room, leave_room
import os
import uuid
from datetime import datetime
import time
import json

app = Flask(__name__, 
            template_folder='../templates',
            static_folder='../static')
app.config['SECRET_KEY'] = 'secret!'
app.config['UPLOAD_FOLDER'] = '../uploads'
app.config['MAX_CONTENT_LENGTH'] = 1000 * 1024 * 1024  # 1000MB 限制

# 确保上传目录存在
if not os.path.exists(app.config['UPLOAD_FOLDER']):
    os.makedirs(app.config['UPLOAD_FOLDER'])

# 初始化Socket.IO
socketio = SocketIO(app, cors_allowed_origins="*")

# 使用固定房间ID，只支持一个房间
DEFAULT_ROOM_ID = 'main_room'

# 存储房间和视频信息
room_info = {
    'video_path': None,
    'video_filename': None,
    'status': 'waiting',  # waiting, playing, paused
    'current_time': 0,
    'created_at': datetime.now().isoformat(),
    # 评分系统字段
    'ratings': {
        'overall': [],  # 总评分列表
        'plot': [],     # 剧情评分列表
        'actors': [],   # 演员评分列表
        'sound': []     # 音效评分列表
    }
}

# 存储分片上传的信息
chunk_uploads = {
    # 'file_id': {
    #     'chunks': {},  # {chunk_index: chunk_path}
    #     'total_chunks': 0,
    #     'filename': '',
    #     'created_at': ''
    # }
}

# 允许的视频文件类型
ALLOWED_EXTENSIONS = {'mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'}

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

# 计算平均评分的辅助函数
def calculate_average_rating(ratings_list):
    if not ratings_list:
        return 0
    return sum(ratings_list) / len(ratings_list)

# 获取所有评分的辅助函数
def get_all_ratings():
    ratings = room_info['ratings']
    return {
        'overall': round(calculate_average_rating(ratings['overall']), 1),
        'plot': round(calculate_average_rating(ratings['plot']), 1),
        'actors': round(calculate_average_rating(ratings['actors']), 1),
        'sound': round(calculate_average_rating(ratings['sound']), 1),
        'count': len(ratings['overall'])
    }

@app.route('/')
def index():
    return render_template('index.html')

# 新增评分提交接口
@app.route('/rating', methods=['POST'])
def submit_rating():
    """提交电影评分"""
    try:
        data = request.get_json()
        if not data:
            return jsonify({'error': '缺少评分数据'}), 400
        
        # 验证评分数据
        required_fields = ['overall', 'plot', 'actors', 'sound']
        for field in required_fields:
            if field not in data or not isinstance(data[field], (int, float)) or data[field] < 1 or data[field] > 10:
                return jsonify({'error': f'无效的{field}评分，必须是1-10之间的数字'}), 400
        
        # 添加评分到列表
        room_info['ratings']['overall'].append(data['overall'])
        room_info['ratings']['plot'].append(data['plot'])
        room_info['ratings']['actors'].append(data['actors'])
        room_info['ratings']['sound'].append(data['sound'])
        
        # 计算新的平均评分
        ratings_data = get_all_ratings()
        
        # 通过Socket.IO广播评分更新
        socketio.emit('rating_update', ratings_data, room=DEFAULT_ROOM_ID)
        
        return jsonify({
            'success': True,
            'message': '评分提交成功',
            'ratings': ratings_data,
            'count': ratings_data['count']
        })
    except Exception as e:
        print(f"评分提交错误: {str(e)}")
        return jsonify({'error': f'评分提交失败: {str(e)}'}), 500

# 新增获取评分接口
@app.route('/rating', methods=['GET'])
def get_rating():
    """获取当前电影评分"""
    try:
        ratings_data = get_all_ratings()
        return jsonify({
            'success': True,
            'ratings': ratings_data,
            'count': ratings_data['count']
        })
    except Exception as e:
        print(f"获取评分错误: {str(e)}")
        return jsonify({'error': f'获取评分失败: {str(e)}'}), 500

@app.route('/upload/init', methods=['POST'])
def init_upload():
    """初始化分片上传，创建文件标识"""
    try:
        data = request.get_json()
        if not data or 'filename' not in data or 'total_chunks' not in data:
            return jsonify({'error': '缺少必要参数'}), 400
        
        filename = data['filename']
        total_chunks = data['total_chunks']
        
        # 检查文件类型
        if not allowed_file(filename):
            return jsonify({'error': '不支持的文件类型'}), 400
        
        # 生成文件ID
        file_id = str(uuid.uuid4())
        
        # 存储上传信息
        chunk_uploads[file_id] = {
            'chunks': {},
            'total_chunks': total_chunks,
            'filename': filename,
            'created_at': datetime.now().isoformat()
        }
        
        return jsonify({
            'success': True,
            'file_id': file_id,
            'message': '上传初始化成功'
        })
    except Exception as e:
        print(f"初始化上传错误: {str(e)}")
        return jsonify({'error': f'初始化上传失败: {str(e)}'}), 500

@app.route('/upload/chunk', methods=['POST'])
def upload_chunk():
    """接收文件分片"""
    try:
        file_id = request.form.get('file_id')
        chunk_index = request.form.get('chunk_index', type=int)
        
        if not file_id or chunk_index is None or file_id not in chunk_uploads:
            return jsonify({'error': '无效的请求参数'}), 400
        
        if 'chunk' not in request.files:
            return jsonify({'error': '没有分片文件'}), 400
        
        chunk_file = request.files['chunk']
        
        # 创建分片存储目录
        chunks_dir = safe_join(app.config['UPLOAD_FOLDER'], 'chunks')
        os.makedirs(chunks_dir, exist_ok=True)
        
        # 保存分片
        chunk_filename = f"{file_id}_{chunk_index}.chunk"
        chunk_path = safe_join(chunks_dir, chunk_filename)
        
        # 流式写入分片
        chunk_size = 8192
        with open(chunk_path, 'wb') as f:
            while True:
                chunk = chunk_file.read(chunk_size)
                if not chunk:
                    break
                f.write(chunk)
        
        # 更新分片信息
        chunk_uploads[file_id]['chunks'][chunk_index] = chunk_path
        
        # 检查是否所有分片都已上传
        is_complete = len(chunk_uploads[file_id]['chunks']) == chunk_uploads[file_id]['total_chunks']
        
        return jsonify({
            'success': True,
            'chunk_index': chunk_index,
            'total_received': len(chunk_uploads[file_id]['chunks']),
            'total_chunks': chunk_uploads[file_id]['total_chunks'],
            'is_complete': is_complete
        })
    except Exception as e:
        print(f"上传分片错误: {str(e)}")
        return jsonify({'error': f'分片上传失败: {str(e)}'}), 500

@app.route('/upload/complete', methods=['POST'])
def complete_upload():
    """完成上传，合并分片"""
    try:
        data = request.get_json()
        if not data or 'file_id' not in data:
            return jsonify({'error': '缺少必要参数'}), 400
        
        file_id = data['file_id']
        if file_id not in chunk_uploads:
            return jsonify({'error': '无效的文件ID'}), 400
        
        upload_info = chunk_uploads[file_id]
        
        # 检查是否所有分片都已上传
        if len(upload_info['chunks']) != upload_info['total_chunks']:
            return jsonify({'error': '分片不完整，无法合并'}), 400
        
        # 准备合并文件
        final_filename = f"video_{upload_info['filename']}"
        final_path = safe_join(app.config['UPLOAD_FOLDER'], final_filename)
        
        # 确保目录存在
        os.makedirs(os.path.dirname(final_path), exist_ok=True)
        
        # 合并分片
        with open(final_path, 'wb') as final_file:
            for i in range(upload_info['total_chunks']):
                if i in upload_info['chunks']:
                    chunk_path = upload_info['chunks'][i]
                    with open(chunk_path, 'rb') as chunk_file:
                        final_file.write(chunk_file.read())
                    # 删除已合并的分片
                    os.remove(chunk_path)
        
        print(f"文件合并完成: {upload_info['filename']}")
        
        # 更新房间信息
        room_info['video_path'] = final_path
        room_info['video_filename'] = upload_info['filename']
        
        # 通知房间内的其他用户
        socketio.emit('video_uploaded', {
            'filename': upload_info['filename']
        }, room=DEFAULT_ROOM_ID)
        
        # 清理上传信息
        del chunk_uploads[file_id]
        
        # 返回JSON响应并确保内容类型正确
        response = jsonify({
            'success': True,
            'filename': upload_info['filename'],
            'message': '文件上传完成'
        })
        response.headers['Content-Type'] = 'application/json'
        return response
    except Exception as e:
        print(f"合并文件错误: {str(e)}")
        # 返回JSON错误响应并确保内容类型正确
        error_response = jsonify({'error': f'文件合并失败: {str(e)}'})
        error_response.headers['Content-Type'] = 'application/json'
        return error_response, 500

@app.route('/videos/<filename>')
def serve_video(filename):
    """流式传输视频文件，支持进度条拖动"""
    try:
        # 使用safe_join确保安全访问
        filepath = safe_join(app.config['UPLOAD_FOLDER'], filename)
        
        # 检查文件是否存在
        if not os.path.exists(filepath):
            return jsonify({'error': '视频文件不存在'}), 404
        
        # 获取文件大小
        file_size = os.path.getsize(filepath)
        
        # 获取Range头
        range_header = request.headers.get('Range', None)
        
        # 如果没有Range头，返回整个文件
        if not range_header:
            return send_from_directory(os.path.dirname(filepath), os.path.basename(filepath))
        
        # 解析Range头
        try:
            start, end = range_header.strip().split('=')[1].split('-')
            start = int(start)
            end = int(end) if end else file_size - 1
        except:
            start = 0
            end = file_size - 1
        
        # 确保范围有效
        if start >= file_size or end >= file_size:
            return Response("Range Not Satisfiable", status=416)
        
        # 计算内容长度
        content_length = end - start + 1
        
        # 设置响应头
        headers = {
            'Content-Range': f'bytes {start}-{end}/{file_size}',
            'Accept-Ranges': 'bytes',
            'Content-Length': content_length,
            'Content-Type': 'video/mp4'
        }
        
        # 以二进制模式打开文件并读取指定范围
        def generate():
            with open(filepath, 'rb') as f:
                f.seek(start)
                remaining = content_length
                while remaining > 0:
                    chunk_size = min(8192, remaining)
                    data = f.read(chunk_size)
                    if not data:
                        break
                    yield data
                    remaining -= chunk_size
                    # 避免CPU占用过高
                    time.sleep(0.001)
        
        # 返回部分内容
        return Response(generate(), 206, headers)
    except Exception as e:
        print(f"视频文件访问错误: {str(e)}")
        return jsonify({'error': '视频文件无法访问'}), 404

@socketio.on('connect')
def handle_connect():
    print('客户端已连接')

@socketio.on('join')
def handle_join():
    join_room(DEFAULT_ROOM_ID)
    # 发送房间当前状态给新加入的用户
    socketio.emit('room_status', room_info, to=request.sid)
    # 发送当前评分信息
    ratings_data = get_all_ratings()
    socketio.emit('rating_update', ratings_data, to=request.sid)
    print(f'用户加入房间: {DEFAULT_ROOM_ID}')

@socketio.on('disconnect')
def handle_disconnect():
    print('客户端已断开连接')

@socketio.on('play')
def handle_play(data):
    current_time = data.get('current_time', 0)
    room_info['status'] = 'playing'
    room_info['current_time'] = current_time
    # 广播播放状态给房间内所有用户，不跳过当前用户，确保所有客户端状态一致
    socketio.emit('play', {'current_time': current_time}, room=DEFAULT_ROOM_ID)
    print(f'播放命令广播: {current_time}秒')

@socketio.on('pause')
def handle_pause(data):
    current_time = data.get('current_time', 0)
    room_info['status'] = 'paused'
    room_info['current_time'] = current_time
    # 广播暂停状态给房间内所有用户，不跳过当前用户
    socketio.emit('pause', {'current_time': current_time}, room=DEFAULT_ROOM_ID)
    print(f'暂停命令广播: {current_time}秒')

@socketio.on('seek')
def handle_seek(data):
    current_time = data.get('current_time', 0)
    room_info['current_time'] = current_time
    # 广播进度更新给房间内所有用户
    socketio.emit('seek', {'current_time': current_time}, room=DEFAULT_ROOM_ID)
    print(f'进度调整广播: {current_time}秒')

@socketio.on('sync_status')
def handle_sync_status(data):
    """处理定期状态同步请求"""
    current_time = data.get('current_time', 0)
    paused = data.get('paused', False)
    
    # 更新房间信息
    room_info['current_time'] = current_time
    if paused:
        room_info['status'] = 'paused'
    else:
        room_info['status'] = 'playing'

if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)